﻿#region using statements

using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;

using NGen.Collections;
using NGen.Managers.GameObject;

using TowerDefense.Objects.Enemies;
using TowerDefense.Objects.Towers;

#endregion

namespace TowerDefense.Misc
{
	/// <summary>
	/// 	Maintains a list of all enemies in the game. Sorts them based on their
	/// 	distance to the end of the path
	/// </summary>
	public class EnemyList : List<GameObjectEnemyBase>
	{
		/// <summary>
		/// 	used to sort the enemies based on distance from the end of the path
		/// </summary>
		private readonly Heap<GameObjectEnemyBase> m_sortingHeap = new Heap<GameObjectEnemyBase>();

		/// <summary>
		/// 	called by towers to target the enemy which is furthest along the path
		/// 	yet still in its range
		/// </summary>
		/// <param name = "p_towerLoc">tower location</param>
		/// <param name = "p_towerRange">tower attack range</param>
		/// <returns>enemy which the tower will target</returns>
		public GameObjectEnemyBase FindTarget(Vector2 p_towerLoc, float p_towerRange) {
			//return if no enemies are alive
			if(Count == 0) {
				return null;
			}

			//clear and populate the heap, it is sorted as things are pushed onto it
			m_sortingHeap.Clear();
			foreach(var obj in this) {
				m_sortingHeap.Push(obj);
			}

			while(m_sortingHeap.Count > 0) {
				//if the root of the heap is within attack range of the tower, return it
				if(Vector2.DistanceSquared(m_sortingHeap.Peek().m_position, p_towerLoc) <
				   Math.Pow(p_towerRange * GameObjectTowerBase.m_attackRadiusTexture.Height / 2, 2.0)) {
					return m_sortingHeap.Peek();
				}

				//else pop the root and loop until no objects are left
				m_sortingHeap.Pop();
			}

			return null;
		}

		/// <summary>
		/// 	add an enemy to the list
		/// </summary>
		/// <param name = "p_enemy">the enemy to add</param>
		public new void Add(GameObjectEnemyBase p_enemy) {
			base.Add(p_enemy);

			//subscribe to the enemy's destroyed event
			p_enemy.OnObjectDestroyedEvent += _EnemyDestroyedEvent;
		}

		/// <summary>
		/// 	callback whenever an enemy is destroyed
		/// </summary>
		/// <param name = "p_enemy">the enemy object destroyed</param>
		private void _EnemyDestroyedEvent(GameObject p_enemy) {
			Remove(p_enemy as GameObjectEnemyBase);
		}
	}
}